<template>
  <view 
    class="fu-form-item"
    :class="{
      'fu-form-item--required': required,
      'fu-form-item--error': validateState === 'error'
    }"
  >
    <view 
      v-if="label || $slots.label"
      class="fu-form-item__label"
      :style="{ width: labelWidth }"
    >
      <slot name="label">{{ label }}</slot>
    </view>
    <view class="fu-form-item__content">
      <slot />
      <text 
        v-if="validateState === 'error'" 
        class="fu-form-item__error"
      >{{ validateMessage }}</text>
    </view>
  </view>
</template>

<script>
import Schema from 'async-validator'
import { getPropByPath } from '../../utils'

/**
 * fu-form-item 表单项组件
 * @description 表单项组件，用于表单验证
 * @property {String} prop - 表单域 model 字段
 * @property {String} label - 标签文本
 * @property {String} labelWidth - 标签宽度，默认单位为rpx
 * @property {Boolean} required - 是否必填
 * @property {Array} rules - 验证规则
 */
export default {
  name: 'fu-form-item',
  props: {
    prop: {
      type: String,
      default: ''
    },
    label: {
      type: String,
      default: ''
    },
    labelWidth: {
      type: String,
      default: '180rpx'
    },
    required: {
      type: Boolean,
      default: false
    },
    rules: {
      type: Array,
      default: () => []
    }
  },
  inject: ['form'],
  data() {
    return {
      validateState: '',
      validateMessage: '',
    }
  },
  computed: {
    labelStyle() {
      const style = {}
      if (this.form.labelPosition === 'left') {
        style.width = this.labelWidth || this.form.labelWidth
      }
      return style
    },
    fieldValue() {
      const model = this.form.model
      if (!model || !this.prop) return null
      let path = this.prop
      if (path.indexOf(':') !== -1) {
        path = path.replace(/:/, '.')
      }
      return getPropByPath(model, path).value
    },
    isRequired() {
      if (this.required !== undefined) {
        return this.required
      }
      if (!this.rules) return false
      const rules = [].concat(this.rules || [])
      return rules.some(rule => rule.required)
    }
  },
  mounted() {
    if (this.prop) {
      this.form.addField(this)
      if (this.required) {
        this.validate('change')
      }
    }
  },
  beforeUnmount() {
    this.form.removeField(this)
  },
  methods: {
    validate(trigger, callback = () => {}) {
      const rules = this.getFilteredRule(trigger)
      if (!rules || rules.length === 0) {
        callback()
        return true
      }

      this.validateState = 'validating'

      const descriptor = {}
      descriptor[this.prop] = rules

      const validator = new Schema(descriptor)
      const model = {}
      model[this.prop] = this.fieldValue

      validator.validate(model, { firstFields: true }, (errors, fields) => {
        this.validateState = !errors ? 'success' : 'error'
        this.validateMessage = errors ? errors[0].message : ''
        callback(this.validateMessage)
      })
    },
    resetField() {
      this.validateState = ''
      this.validateMessage = ''

      const model = this.form.model
      const value = this.fieldValue
      let path = this.prop
      if (path.indexOf(':') !== -1) {
        path = path.replace(/:/, '.')
      }

      const prop = getPropByPath(model, path)
      if (Array.isArray(value)) {
        prop.target[prop.key] = [].concat(this.initialValue)
      } else {
        prop.target[prop.key] = this.initialValue
      }
    },
    clearValidate() {
      this.validateState = ''
      this.validateMessage = ''
    },
    getFilteredRule(trigger) {
      const rules = this.form.rules[this.prop]
      return [].concat(rules || []).filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1)
    }
  }
}
</script>

<style lang="scss" scoped>
.fu-form-item {
  display: flex;
  align-items: flex-start;
  padding: 24rpx 0;
  font-size: var(--fu-font-size-base);
  line-height: 1.5;
  
  &--required {
    .fu-form-item__label {
      &::before {
        content: '*';
        color: var(--fu-danger-color);
        margin-right: 4rpx;
        vertical-align: middle;
      }
    }
  }
  
  &--error {
    .fu-form-item__content {
      input,
      textarea {
        border-color: var(--fu-danger-color) !important;
      }
    }
  }
  
  &__label {
    flex-shrink: 0;
    padding-right: 24rpx;
    color: var(--fu-text-color);
    line-height: 88rpx;
    box-sizing: border-box;
  }
  
  &__content {
    flex: 1;
    min-height: 88rpx;
    position: relative;
  }
  
  &__error {
    position: absolute;
    left: 0;
    bottom: -36rpx;
    color: var(--fu-danger-color);
    font-size: var(--fu-font-size-sm);
    transform: translateY(100%);
    white-space: nowrap;
    
    &:not(:empty) {
      margin-top: 4rpx;
    }
  }
}
</style> 